package org.mzyyn.qr.service.user.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil;
import com.alibaba.fastjson2.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.mzyyn.qr.constants.RedisKeyAppletsConstant;
import org.mzyyn.qr.constants.RedisKeyConstant;
import org.mzyyn.qr.context.UserAuthContext;
import org.mzyyn.qr.dto.user.Code2sessionLoginDTO;
import org.mzyyn.qr.dto.user.UserProfileLoginDTO;
import org.mzyyn.qr.dto.user.UserRegisterInfoDTO;
import org.mzyyn.qr.entity.UserPO;
import org.mzyyn.qr.enums.SourceStatusEnum;
import org.mzyyn.qr.mapper.UserMapper;
import org.mzyyn.qr.service.user.UserService;
import org.mzyyn.qr.service.user.UserAppletsService;
import org.mzyyn.qr.vo.user.LoginUserInfoVO;
import org.mzyyn.qr.vo.user.LoginUserVO;
import org.mzyyn.qr.vo.user.SessionInfoVO;
import org.mzyyn.qr.vo.user.UserInfoRes;
import org.qr.xiaocao.common.constants.NumberConstant;
import org.qr.xiaocao.common.enums.CommonResultEnum;
import org.qr.xiaocao.exception.ServiceException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * @author lipeng
 * @description 用户服务
 * @date 2024/9/3 9:40
 */
@Service
@Slf4j
public class UserServiceImpl extends ServiceImpl<UserMapper, UserPO> implements UserService {
    @Qualifier("userWxAppletsServiceImpl")
    @Autowired
    private UserAppletsService userWxAppletsService;

    @Qualifier("userQqAppletsServiceImpl")
    @Autowired
    private UserAppletsService userQqAppletsService;

    @Qualifier("userH5AppletsServiceImpl")
    @Autowired
    private UserAppletsService userH5AppletsService;

    @Resource
    StringRedisTemplate stringRedisTemplate;

    private UserAppletsService lookup(){
        if(SourceStatusEnum.WX.getCode().equals(UserAuthContext.getUserInfo().getSource())){
            return userWxAppletsService;
        }else if(SourceStatusEnum.H5.getCode().equals(UserAuthContext.getUserInfo().getSource())){
            return userH5AppletsService;
        }else {
            return userQqAppletsService;
        }
    }

    @Override
    public UserPO findByUser(String phoneOrOpenid) {
        if (StrUtil.isEmpty(phoneOrOpenid)) {
            return null;
        }
        return lookup().findByUser(phoneOrOpenid);
    }

    @Override
    public UserPO findByUserByUuid(String uuid) {
        if (StrUtil.isEmpty(uuid)) {
            return null;
        }
        LambdaQueryWrapper<UserPO> userQuery = new LambdaQueryWrapper<>();
        userQuery.eq(UserPO::getUuid, uuid);
        UserPO userPO = this.baseMapper.selectOne(userQuery);
        if( null == userPO ){
            return null;
        }

        Object redisIntegral = stringRedisTemplate.opsForHash().get(
                RedisKeyConstant.USER_CORE_MAP + uuid,
                RedisKeyConstant.USER_JIFEN_MAP_JIFEN
        );
        if( null != redisIntegral ){
            userPO.setIntegral(Integer.parseInt(String.valueOf(redisIntegral)));
        }
        if( null == userPO.getIntegral() ){
            userPO.setIntegral(0);
        }
        return userPO;
    }

    @Override
    public LoginUserInfoVO appletUnionidLogin(Code2sessionLoginDTO code2sessionLoginDTO) {
        SessionInfoVO sessionInfo = lookup().appletGetSessionByCode(code2sessionLoginDTO);
        LoginUserInfoVO loginUserInfoVO = new LoginUserInfoVO();
//        loginUserInfoVO.setOpenId(sessionInfo.getOpenid());
//        loginUserInfoVO.setUnionId(sessionInfo.getUnionid());

        UserPO userInfo = findByUser(sessionInfo.getOpenid());
        if (null == userInfo) {
            return loginUserInfoVO;
        }
        loginUserInfoVO.setLoginUser(BeanUtil.toBean(userInfo, LoginUserVO.class));

        UserInfoRes userInfoRes = transformUserInfoRes(userInfo);

        String appletsToken = getAppletsToken(userInfo.getQqOpenid() + userInfo.getId());

        //缓存24小时
        stringRedisTemplate.opsForValue().set(
                RedisKeyAppletsConstant.APPLETS_TOKEN_USER_INFO + appletsToken,
                JSON.toJSONString(userInfoRes),
                NumberConstant.INT_TWENTY_FOUR,
                TimeUnit.HOURS
        );
        stringRedisTemplate.opsForHash().put(
                RedisKeyConstant.USER_CORE_MAP + userInfoRes.getUuid(),
                RedisKeyConstant.USER_JIFEN_MAP_JIFEN,
                StrUtil.toStringOrNull(userInfoRes.getIntegral())
        );
        stringRedisTemplate.expire(RedisKeyConstant.USER_CORE_MAP + userInfoRes.getUuid(), NumberConstant.INT_TWENTY_FOUR, TimeUnit.HOURS);

        loginUserInfoVO.setAccessToken(appletsToken);

        return loginUserInfoVO;
    }

    @Override
    public LoginUserInfoVO registerAndLogin(UserProfileLoginDTO userProfileLoginDTO) {
        UserRegisterInfoDTO registerInfo = lookup().getRegisterInfo(userProfileLoginDTO);
        if( StrUtil.isEmpty(userProfileLoginDTO.getUserName()) ){
            registerInfo.setUserName(RandomUtil.randomString(8));
        }
        if( StrUtil.isEmpty(userProfileLoginDTO.getAvatar()) ){
            registerInfo.setAvatar(StrUtil.EMPTY);
        }
        UserPO userInfo = register(registerInfo);

        UserInfoRes userInfoRes = transformUserInfoRes(userInfo);

        String appletsToken = getAppletsToken(userInfo.getH5Openid());

        //缓存24小时
        stringRedisTemplate.opsForValue().set(
                RedisKeyAppletsConstant.APPLETS_TOKEN_USER_INFO + appletsToken,
                JSON.toJSONString(userInfoRes),
                NumberConstant.INT_TWENTY_FOUR,
                TimeUnit.HOURS
        );
        stringRedisTemplate.opsForHash().put(
                RedisKeyConstant.USER_CORE_MAP + userInfoRes.getUuid(),
                RedisKeyConstant.USER_JIFEN_MAP_JIFEN,
                String.valueOf(userInfoRes.getIntegral())
        );
        stringRedisTemplate.expire(RedisKeyConstant.USER_CORE_MAP + userInfoRes.getUuid(), NumberConstant.INT_TWENTY_FOUR, TimeUnit.HOURS);

        LoginUserInfoVO loginUserInfoVO = new LoginUserInfoVO();
        loginUserInfoVO.setAccessToken(appletsToken);
        loginUserInfoVO.setLoginUser(BeanUtil.toBean(userInfo, LoginUserVO.class));

        return loginUserInfoVO;
    }

    @Override
    public String getUserCoreInfo(String uuid,String mapKey){
        if( StrUtil.isEmpty(uuid) ) {
            uuid = UserAuthContext.getUserInfo().getUuid();
        }
        return (String) stringRedisTemplate.opsForHash().get(RedisKeyConstant.USER_CORE_MAP + uuid, mapKey);
    }

    @Override
    public Map<Object,Object> getUserCoreInfo(String uuid){
        if( StrUtil.isEmpty(uuid) ) {
            uuid = UserAuthContext.getUserInfo().getUuid();
        }
        return stringRedisTemplate.opsForHash().entries(RedisKeyConstant.USER_CORE_MAP + uuid);
    }

    @Override
    public Long getUserIntegral(String uuid) {
        String userIntegralStr = getUserCoreInfo(uuid,RedisKeyConstant.USER_JIFEN_MAP_JIFEN);
        if( StrUtil.isEmpty(userIntegralStr) ){
            return 0L;
        }
        return Long.parseLong(userIntegralStr);
    }

    @Override
    public Long getUserDongjieIntegral(String uuid,String goodsId) {
        if( StrUtil.isEmpty(goodsId) ){
            return 0L;
        }
        String mapKey = String.format(RedisKeyConstant.USER_JIFEN_MAP_DONGJIE_JIFEN, goodsId);

        String userDongjieIntegralStr = getUserCoreInfo(uuid,mapKey);
        if( StrUtil.isEmpty(userDongjieIntegralStr) ){
            return 0L;
        }
        return Long.parseLong(userDongjieIntegralStr);
    }

    @Override
    public LoginUserVO getUserInfo() {
        UserInfoRes userInfo = UserAuthContext.getUserInfo();

        LoginUserVO loginUserVO = new LoginUserVO();
        loginUserVO.setUsername(userInfo.getUsername());
        loginUserVO.setRealname(userInfo.getUsername());
        loginUserVO.setPhone(userInfo.getPhone());
        loginUserVO.setAvatar(userInfo.getAvatar());
        loginUserVO.setAuthFlag(userInfo.getAuthFlag());
        loginUserVO.setQqOpenid(userInfo.getQqOpenid());

        Object integral = stringRedisTemplate.opsForHash().get(
                RedisKeyConstant.USER_CORE_MAP + userInfo.getUuid(),
                RedisKeyConstant.USER_JIFEN_MAP_JIFEN
        );
        loginUserVO.setIntegral(integral == null ? 0 : Integer.parseInt(integral.toString()));
//        loginUserVO.setWxOpenid(userInfo.getWxOpenid());
//        loginUserVO.setQqOpenid(userInfo.getQqOpenid());


        return loginUserVO;
    }

    @Override
    public Integer addIntegral(Integer integral,String uuid) {
        if(  integral >= 100000  ){
            throw new ServiceException(CommonResultEnum.COMMON_FAILED.getCode(),"积分过大，请联系客服处理");
        }


        if( StrUtil.isEmpty(uuid) ){
            uuid = UserAuthContext.getUserInfo().getUuid();
        }

        log.info("用户uuid:{}增加积分{}",uuid,integral);

        UserPO user = findByUserByUuid(uuid);
        Integer lastIntegral = user.getIntegral() + integral;

        user.setIntegral(lastIntegral);
        user.setUpdateTime(LocalDateTime.now());

        this.baseMapper.updateById(user);

        stringRedisTemplate.opsForHash().put(
                RedisKeyConstant.USER_CORE_MAP + uuid,
                RedisKeyConstant.USER_JIFEN_MAP_JIFEN,
                String.valueOf(lastIntegral)
        );


        return lastIntegral;
    }

    @Override
    public String getH5Code() {
        return UUID.randomUUID().toString().replace("-","");
    }

    /**
     * @return com.sinochem.map.iot.uav.biz.entity.SysUserPO
     * @descrition 注册，存在就判断是否更新，不存在就注册
     * @author lipeng
     * @date 2024/1/26
     */
    private UserPO register(UserRegisterInfoDTO registerInfo) {
        String userName = registerInfo.getUserName();
        String avater = registerInfo.getAvatar();
        String phone = registerInfo.getPhone();
        String unionid = registerInfo.getUnionid();
        String qqopenid = registerInfo.getQqopenid();
        String wxopenid = registerInfo.getWxopenid();
        String h5openid = registerInfo.getH5openid();

        String uuid = phone;
        if( StrUtil.isEmpty(uuid) ){
            uuid = qqopenid;
        }
        if( StrUtil.isEmpty(uuid) ){
            uuid = wxopenid;
        }
        if( StrUtil.isEmpty(uuid) ){
            uuid = h5openid;
        }

        UserPO user = findByUser(uuid);

        if (null == user) {
            //注册
            user = new UserPO();
            user.setUsername(userName);
            user.setUuid(UUID.randomUUID().toString().replace("-",""));
            user.setPhone(phone);
            user.setUnionid(unionid);
            user.setQqOpenid(qqopenid);
            user.setWxOpenid(wxopenid);
            user.setH5Openid(h5openid);
            user.setAvatar(avater);
            user.setIntegral(0);
            user.setCreateTime(LocalDateTime.now());
            user.setUpdateTime(LocalDateTime.now());
            try {
                this.baseMapper.insert(user);
            } catch (Exception e) {
                log.error("用户注册失败：{}",e);
                throw new ServiceException(CommonResultEnum.INSERT_ERROR.getCode(), "用户注册失败");
            }
        }
        if (StrUtil.isEmpty(user.getQqOpenid()) && StrUtil.isNotEmpty(qqopenid)) {
            user.setUnionid(unionid);
            user.setQqOpenid(qqopenid);
            user.setUsername(userName);
            user.setRealname(userName);
            this.baseMapper.updateById(user);
        }
        if (StrUtil.isEmpty(user.getWxOpenid()) && StrUtil.isNotEmpty(wxopenid)) {
            user.setUnionid(unionid);
            user.setWxOpenid(wxopenid);
            user.setUsername(userName);
            user.setRealname(userName);
            this.baseMapper.updateById(user);
        }
        return user;
    }

    /**
     * @param sysUser
     * @return com.sinochem.map.iot.uav.biz.feign.platform.res.UserInfoRes
     * @descrition 转换为统一的用户res
     * @author lipeng
     * @date 2024/2/26
     */
    private UserInfoRes transformUserInfoRes(UserPO sysUser) {
        return BeanUtil.toBean(sysUser, UserInfoRes.class);
    }

    /**
     * @param openid
     * @return java.lang.String
     * @descrition 获取小程序访问token
     * @author lipeng
     * @date 2024/1/26
     */
    private String getAppletsToken(String openid) {
        return SecureUtil.md5(openid);
    }

}
